from ctypes import *
import time
# import sys
from func_timeout import func_set_timeout
import numpy as np
import struct
import ctypes
# import re





VCI_USBCAN2 = 4
STATUS_OK = 1
class VCI_INIT_CONFIG(Structure):
    _fields_ = [("AccCode", c_uint),
                ("AccMask", c_uint),
                ("Reserved", c_uint),
                ("Filter", c_ubyte),
                ("Timing0", c_ubyte),
                ("Timing1", c_ubyte),
                ("Mode", c_ubyte)
                ]
class VCI_CAN_OBJ(Structure):
    _fields_ = [("ID", c_uint),
                ("TimeStamp", c_uint),
                ("TimeFlag", c_ubyte),
                ("SendType", c_ubyte),
                ("RemoteFlag", c_ubyte),
                ("ExternFlag", c_ubyte),
                ("DataLen", c_ubyte),
                ("Data", c_ubyte*8),
                ("Reserved", c_ubyte*3)
                ]

class VCI_CAN_OBJ_ARRAY(Structure):
    _fields_ = [('SIZE', ctypes.c_uint16), ('STRUCT_ARRAY', ctypes.POINTER(VCI_CAN_OBJ))]
    def __init__(self,num_of_structs):
                                                                 # this bracket is indispensable
        self.STRUCT_ARRAY = ctypes.cast((VCI_CAN_OBJ * num_of_structs)(),ctypes.POINTER(VCI_CAN_OBJ))
        self.SIZE = num_of_structs
        self.ADDR = self.STRUCT_ARRAY[0]

def try_trans_uint(num_str:str):
    if(len(num_str)>2):
        if(num_str[:2]=='0x'):
            try:
                num=int(num_str[2:],16)
            except:
                return
        elif(num_str[:2]=='0b'):
            try:
                num=int(num_str[2:],2)
            except:
                return
        else:
            try:
                num=int(num_str)
            except:
                return
    else:
        try:
            num=int(num_str)
        except:
            return
    return num

def try_trans_signedHex(num_str:str):
    signed=False
    if(num_str[0]=="-"):
        signed=True
        num_str=num_str[1:]
    if(len(num_str)>2):
        if(num_str[:2]=='0x'):
            try:
                num=int(num_str[2:],16)
            except:
                return
        elif(num_str[:2]=='0b'):
            try:
                num=int(num_str[2:],2)
            except:
                return
        else:
            try:
                num=int(num_str)
            except:
                return
    else:
        try:
            num=int(num_str)
        except:
            return
    if(signed):
        return hex(0x100000000-num)[-8:],-num
    else:
        return hex(num)[2:].rjust(8,'0'),num



def dac8568_init(cmd_code:list=["04","05"]):
    cmd_ls=[]
    for code in cmd_code:
        cmd_ls.append("05"+code+"000007000000")
        cmd_ls.append("05"+code+"00000a000000")
        cmd_ls.append("05"+code+"0000090a0000")
        # cmd_ls.append("05"+code+"000008000001")
        cmd_ls.append("05"+code+"0000040000ff")
        # cmd_ls.append("05"+code+"000005000000")
    return cmd_ls


cols_ls=['k','yellow','r','darkorange',
      'peru','chartreuse','y','cyan',
      'dodgerblue','navy','b','gray',
      'blueviolet','indigo','magenta','pink']


startBias_dict={
    "CSA_VREST4(mV)" :["05040000030",572 ],
    "CSA_VREST3(mV)" :["05040000031",572 ],
    "CSA_VREST2(mV)" :["05040000032",572 ],
    "CSA_VREST1(mV)" :["05040000033",572 ],
    "CSA_VREST0(mV)" :["05040000034",572 ],
    "ISFBN_IN(mV)"   :["05040000035",100 ],
    "VSFBN_IN(mV)"   :["05040000036",977 ],
    "CSA_VREF(mV)"   :["05040000037",403 ],
    "VCASN(mV)"      :["05050000030",554 ],
    "VSFBP_IN(mV)"   :["05050000031",860 ],
    "VBIASP_IN(mV)"  :["05050000032",899 ],
    "VBIASN_IN(mV)"  :["05050000033",1057],
    "ISFBP_IN(mV)"   :["05050000034",100 ],
    "IBIASP_IN(mV)"  :["05050000035",100 ],
    "IBIASN_IN(mV)"  :["05050000036",100 ],
    "VOFF(mV)"       :["05050000037",1200],
    "GMCP_THR(mV)"   :["050e0000003",1200],
}

#dac电压配置指令生成
#cmd_front:该通道的特定指令（以字符串表示的16进制数）,vol_set:该通道的电压配置（浮点型，单位mV）
#vref:参考电压（浮点型，单位mV）,vol_limit：上限电压（浮点型，单位mV）
def set_vol_mv(cmd_key:str,vol_set:float,vref:float=2500.0,vol_limit=2500):# 单位mV
    vol_set=float(vol_set)
    cmd_front=startBias_dict[cmd_key][0]
    if(vol_set > vol_limit):
        vol_set=vol_limit
        print(f"over limit! set to {vol_limit} mV")
    elif(vol_set < 0):
        vol_set=0
        print(f"can\'t set negitive voltage! set to 0 mV")
    cmd_str=cmd_front+(hex(int(65536*vol_set/vref))[2:]).rjust(4,'0')+'0'
    return cmd_str


startTML_dict={
    'row_start2'           :['01',3   ],
    'col_start2'           :['02',3   ],
    'row_end2'             :['03',348 ],
    'col_end2'             :['04',503 ],
    'row_step2'            :['05',5   ],
    'col_step2'            :['06',5   ],
    'adc_delay'            :['07',14  ],
    'threshold'            :['08',400 ],
    'debugmode'            :['09',0   ],
    'multiple_adc_pix'     :['0a',4   ],
    'shaodian_scan_colnum' :['0b',101 ],
    'shaodian_scan_num'    :['0c',7070],
    'block_pix_num'        :['0d',40  ],
    'shaodian_dout_en'     :['0e',0   ],
    'bakgnd_thr'           :['0f',100 ],
    'rst_load_time'        :['ff',40  ]
# rst_load_time is config update command, must be the last
}


def set_tml(cmd_key,value):
    cmd_front=startTML_dict[cmd_key][0]
    value=try_trans_uint(value)
    
    return '050d'+cmd_front[-2:]+'00'+hex(value)[2:].rjust(8,'0')


tml_data_enSet=["05020000000000ff","0502000000000000"]
mon_data_enSet=["050c0000000000ff","050c000000000000"]
shutdownlist=[
    "0502000000000000",
    "050c000000000000",
    "05a1a10000000000",
    "0504000010ffffff",#power down dac8568
    "0505000010ffffff",#power down dac8568
    "0507555555555555"
]

startCompressdict={
    "pixel_thr"  :["00",30   ],
    "frame_thr"  :["01",150 ],
    "mask_en"    :["02",1 ]
}


def set_addrNum(cmd_key,value):
    cmd_front=startCompressdict[cmd_key][0]
    value=int(value)
    return '0510'+cmd_front[-2:]+'00'+hex(value)[2:].rjust(8,'0')


hv_module_dic={
    "0560N":[6000,167],
    "0550N":[5000,200]
}


glb_hvModuleType=""
start_hvProtect={
    "hv_safe_voltage(V)"     :["00",2000],
    "Idiv_threshold(adc)"    :["01",2000],
    "time_threshold(ms)"     :["02",100],
    "protect_chance_num"     :["03",3],
    "safe_period(s)"         :["04",10]
}

def set_hvProtect(cmd_key,value,hvModType):
    max_V,max_I=hv_module_dic[hvModType]
    cmd_front=start_hvProtect[cmd_key][0]
    if(cmd_key=="hv_safe_voltage(V)"):
        value=float(value)
        if(value<0):value=2500
        value=int((value/max_V)*4096)
        return '0508'+cmd_front[-2:]+'00'+hex(value)[2:].rjust(8,'0')
    elif(cmd_key=="Idiv_threshold(adc)"):
        value=try_trans_uint(value)
        return '0508'+cmd_front[-2:]+'00'+hex(value)[2:].rjust(8,'0')
    elif(cmd_key=="time_threshold(ms)"):
        value=float(value)*100_000
        value=int(value)
        return '0508'+cmd_front[-2:]+'00'+hex(value)[2:].rjust(8,'0')
    elif(cmd_key=="protect_chance_num"):
        value=try_trans_uint(value)
        return '0508'+cmd_front[-2:]+'00'+hex(value)[2:].rjust(8,'0')
    elif(cmd_key=="safe_period(s)"):
        value=float(value)*100_000_000
        value=int(value)
        return '0508'+cmd_front[-2:]+'00'+hex(value)[2:].rjust(8,'0')
    print("error")


firmware_state_dic = {
    '00' : '上电未配置',
    '01' : '上电配置完成',
    '02' : '正常运行',
    '03' : '数传状态',
    '04' : '固件更新状态',
    '05' : '预掉电',
    '06' : '高压设置',
    '07' : '取数使能',
    '08' : 'DAC配置'
}


class cxpd_can():
    def __init__(self):
        self.canDLL = windll.LoadLibrary('./ControlCAN.dll') # for python x64
        self.can_sate=self.can_start()
        self.hv_module_type="0560N"
    
    def can_start(self):
        # device open
        ret = self.canDLL.VCI_OpenDevice(VCI_USBCAN2, 0, 0)
        if ret != STATUS_OK:
            print('VCI_OpenDevice open ERROR\r\n')
            return False
        # device channel 0 initial
        vci_initconfig = VCI_INIT_CONFIG(0x80000000, 0xFFFFFFFF, 0,0, 0x00, 0x1c, 0)# baud 500k，normal mod
        ret = self.canDLL.VCI_InitCAN(VCI_USBCAN2, 0, 0, byref(vci_initconfig))
        if ret != STATUS_OK:
            print('VCI_InitCAN1 config ERROR\r\n')
            return False
        # CAN channel start
        ret = self.canDLL.VCI_StartCAN(VCI_USBCAN2, 0, 0)
        if ret != STATUS_OK:
            print('VCI_StartCAN1 start ERROR\r\n')
            return False
        print('VCI_StartCAN1 start SUCCESS\r\n')
        return True

    def can_post64(self,cmd_temp:bytes):
        cmd_temp+=bytes.fromhex('0000000000000000')
        ubyte_array = c_ubyte*8
        a = ubyte_array(cmd_temp[0],cmd_temp[1],cmd_temp[2],cmd_temp[3],cmd_temp[4],cmd_temp[5],cmd_temp[6],cmd_temp[7])
        ubyte_3array = c_ubyte*3
        b = ubyte_3array(0, 0 , 0)
        #ID; TimeStamp; TimeFlag; SendType; RemoteFlag; ExternFlag; DataLen; Data[8]; Reserved[3]
        vci_can_obj = VCI_CAN_OBJ(0x70200, 0, 0, 0, 0, 1,  8, a, b)
        ret = self.canDLL.VCI_Transmit(VCI_USBCAN2, 0, 0, byref(vci_can_obj), 1)
        return time.time()
    
    def utc_pos(self):
        self.cxpd_cmd_str('5000'+hex(int(time.time()))[2:].rjust(8,'0')+'0000')
        # self.cxpd_cmd_str('5005'+hex(int(time.time()))[2:].rjust(8,'0')+'0000')
    
    def cxpd_cmd_str(self,cmd_str:str):
        if(len(cmd_str)==16):
            try:
                print(f"send command:{cmd_str}")
                cmd=bytes.fromhex(cmd_str)
                self.can_post64(cmd)
            except:
                print(f"cmd {cmd_str} error!")
        else:print(f"len of cmd is 16, instead of {len(cmd_str)}({cmd_str})")
            

    
    def cxpd_mon_post(self):
        ubyte_array = c_ubyte*8
        a = ubyte_array(0x05,0x00,0x55,0x55,0x55,0x55,0x55,0x55)
        ubyte_3array = c_ubyte*3
        b = ubyte_3array(0, 0 , 0)
        #ID; TimeStamp; TimeFlag; SendType; RemoteFlag; ExternFlag; DataLen; Data[8]; Reserved[3]
        vci_can_obj = VCI_CAN_OBJ(0x70200, 0, 0, 0, 0, 1,  8, a, b)
        ret = self.canDLL.VCI_Transmit(VCI_USBCAN2, 0, 0, byref(vci_can_obj), 1)
        return time.time()
    
    def cxpd02_get_tele(self,time_post,frame_num,timeout:c_uint=0.05) :
        rx_vci_can_obj = VCI_CAN_OBJ_ARRAY(2500)
        resp_temp=bytes()
        num_rx=0
        while num_rx < frame_num:
            if time.time() - time_post < timeout :
                ret = self.canDLL.VCI_Receive(VCI_USBCAN2, 0, 0, byref(rx_vci_can_obj.ADDR), 2500, 0)
                if(ret>0):
                    num_rx+=1
                    resp_temp+=rx_vci_can_obj.STRUCT_ARRAY[0].Data
            else :
                break
        # print(f"resp_len:{len(resp_temp)},num_frame:{num_rx}\nbytes:{resp_temp.hex()}")
        return resp_temp
    
    def cxpd02_mon_get(self,debug=False):
        if(debug):
            print("debug mon get")
            return [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31]
        self.cxpd_mon_post()
        resp_temp=self.cxpd02_get_tele(time.time(),8,0.05)
        data=np.frombuffer(resp_temp,dtype='B')
        # for i in range(int(len(resp_temp)/8)):
        #     print(resp_temp[i*8:i*8+8].hex())
        softsum_cheak=np.bitwise_and(data[:-1].sum(),0xff)
        if(len(resp_temp)==56):
            byte_len, data_type, data_code, reps_cnt, right_frame_cnt, error_frame_cnt, res_cmd=struct.unpack('!H6B',resp_temp[0:8])#frame0
            # bme680_gas_p1, bme680_gas_pt, bme680_gas_t0, gmpd_trig_rate=struct.unpack('!4H',resp_temp[8:16])#frame1
            # print(bme680_gas_p1, bme680_gas_pt, bme680_gas_t0, gmpd_trig_rate)
            bme680_gas_press=int.from_bytes(resp_temp[8:11],byteorder='big')
            bme680_gas_temp=int.from_bytes(resp_temp[11:14],byteorder='big')
            gmpd_trig_rate=int.from_bytes(resp_temp[14:16],byteorder='big')
            temp=struct.unpack('!Q',resp_temp[16:24])[0]#frame2
            hv_mon_voltage=(temp>>52)&0xfff
            hv_mon_current=(temp>>40)&0xfff
            hv_power_voltage=(temp>>28)&0xfff
            hv_power_current=(temp>>16)&0xfff
            hv_state=temp&0xffff
            sts_40_temp, self_cheak, powerin_volatge, powerin_current=struct.unpack('!4H',resp_temp[24:32])#frame3
            emmc_argue_wr, emmc_argue_rd=struct.unpack('!2I',resp_temp[32:40])#frame4
            ota_flag, udp_flag, firmware_state, firmware_version, seu_rom_err_cnt, seu_err_cnt, = struct.unpack('!3BHBH',resp_temp[40:48])#frame5
            seu_tmr_err_cnt, utc_data, reserve_data, sum_cheak = struct.unpack('!HIBB',resp_temp[48:56])#frame6
            # print(f"byte_len:{byte_len}, data_type:{data_type}, data_code:{data_code}, reps_cnt:{reps_cnt}, right_frame_cnt:{right_frame_cnt}, error_frame_cnt:{error_frame_cnt}, res_cmd:{res_cmd}, gmpd_trig_rate:{gmpd_trig_rate}, hv_mon_voltage:{hv_mon_voltage}, hv_mon_current:{hv_mon_current}, hv_power_voltage:{hv_power_voltage}, hv_power_current:{hv_power_current}, powerin_volatge:{powerin_volatge}, powerin_current:{powerin_current}, sts_40_temp:{sts_40_temp}, seu_err_cnt:{seu_err_cnt}, seu_tmr_err_cnt:{seu_tmr_err_cnt}, seu_rom_err_cnt:{seu_rom_err_cnt}, ota_flag:{ota_flag}, emmc_argue_wr:{emmc_argue_wr}, emmc_argue_rd:{emmc_argue_rd}, udp_flag:{udp_flag}, firmware_state:{firmware_state}, firmware_version:{firmware_version}, utc_data:{utc_data}, cheak_state:{self_cheak}, sum_cheak:{sum_cheak}, bme680_gas_temp:{bme680_gas_temp}, bme680_gas_press:{bme680_gas_press}")
            print("sum cheak:",sum_cheak,softsum_cheak)
            ls_raw=list([byte_len, data_type, data_code, reps_cnt, right_frame_cnt, error_frame_cnt, res_cmd,
                    bme680_gas_press, bme680_gas_temp, gmpd_trig_rate,
                    hv_mon_voltage, hv_mon_current, hv_power_voltage, hv_power_current, hv_state,
                    sts_40_temp, self_cheak, powerin_volatge, powerin_current,
                    emmc_argue_wr, emmc_argue_rd,
                    ota_flag, udp_flag, firmware_state, firmware_version, seu_rom_err_cnt, seu_err_cnt,
                    seu_tmr_err_cnt, utc_data, sum_cheak])
            bme680_gas_press = (bme680_gas_press-1.4e7)/(-92.7)
            bme680_gas_temp = (bme680_gas_temp-6559968)/51797
            sts_40_temp = -45+175*sts_40_temp/65535
            powerin_volatge=powerin_volatge*1.25
            if(hv_mon_current!=0 and hv_mon_voltage!=0):
                if(self.hv_module_type=="0550N"):
                    hv_mon_voltage=(hv_mon_voltage-12.553)/0.8124
                    hv_mon_current=(hv_mon_current*200)/4096
                elif(self.hv_module_type=="0560N"):
                    hv_mon_voltage=(hv_mon_voltage-7.7302)/0.6813
                    hv_mon_current=hv_mon_current*167/4096
                else:
                    hv_mon_voltage="type error"
                    hv_mon_current="type error"
                # hv_mon_voltage=hv_mon_voltage*2/4096
                # hv_mon_current=hv_mon_current*2/4096
            firmware_state=hex(firmware_state)[2:].rjust(2,'0')
            firmware_state=firmware_state_dic[firmware_state]
            utc_data=time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(utc_data))
            ls_trans=list([byte_len, data_type, data_code, reps_cnt, right_frame_cnt, error_frame_cnt, res_cmd,
                    bme680_gas_press, bme680_gas_temp, gmpd_trig_rate,
                    hv_mon_voltage, hv_mon_current, hv_power_voltage, hv_power_current, hv_state,
                    sts_40_temp, self_cheak, powerin_volatge, powerin_current,
                    emmc_argue_wr, emmc_argue_rd,
                    ota_flag, udp_flag, firmware_state, firmware_version, seu_rom_err_cnt, seu_err_cnt,
                    seu_tmr_err_cnt, utc_data, sum_cheak])
            return [ls_trans,ls_raw]
        print(f"error:only resive {len(resp_temp)} bytes")
        return False